package com.icesoft.system.auth.service;

import com.icesoft.framework.core.cache.RedisCacheManager;
import com.icesoft.framework.core.exception.BusinessException;
import com.icesoft.system.auth.AuthProperties;
import com.icesoft.system.auth.credential.EmailCredential;
import com.icesoft.system.auth.credential.PhoneCredential;
import com.icesoft.system.auth.credential.PwdCredential;
import com.icesoft.system.auth.entity.AuthToken;
import com.icesoft.system.auth.entity.LoginUser;
import com.icesoft.system.entity.Account;
import com.icesoft.system.entity.BaseLoginUser;
import com.icesoft.system.service.impl.AccountServiceImpl;
import com.icesoft.system.service.impl.UserServiceImpl;
import com.icesoft.system.util.JwtUtil;
import com.icesoft.system.util.SecurityUtils;
import com.icesoft.system.vo.UserVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Service;

import java.util.Set;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@RequiredArgsConstructor
public class LoginService {

	private final AccountServiceImpl accountService;

	private final AuthProperties authProperties;

	private final RedisCacheManager redisCacheManager;

	private final UserServiceImpl userService;

	public UserVO pwdCredentialLogin(PwdCredential pwdCredential) throws Exception {
		if (pwdCredential.getUsername() == null || pwdCredential.getPassword() == null) {
			throw new BusinessException("账号密码不能为空");
		}
		Account account = accountService.findByUsername(pwdCredential.getUsername());
		if (account == null) {
			throw new BusinessException("账号不存在");
		}
		UserVO user = userService.getViewObject(account.getUserId(), UserVO.class);
		if (user == null) {
			throw new BusinessException("账号不存在");
		}
		String token = this.createTokenAndLogin(account, user);
		user.setToken(token);
		userService.updateEntity(user);
		return user;
	}

	public UserVO phoneCredentialLogin(PhoneCredential pwdCredential) throws Exception {
		if (pwdCredential.getPhone() == null || pwdCredential.getCode() == null) {
			throw new BusinessException("账号密码不能为空");
		}
		Account account = accountService.findByUsername(pwdCredential.getPhone());
		if (account == null) {
			throw new BusinessException("账号不存在");
		}
		UserVO user = userService.getViewObject(account.getUserId(), UserVO.class);
		if (user == null) {
			throw new BusinessException("账号不存在");
		}
		String token = this.createTokenAndLogin(account, user);
		user.setToken(token);
		userService.updateEntity(user);
		return user;
	}

	public UserVO emailCredentialLogin(EmailCredential pwdCredential) throws Exception {
		if (pwdCredential.getEmail() == null || pwdCredential.getCode() == null) {
			throw new BusinessException("账号密码不能为空");
		}
		Account account = accountService.findByUsername(pwdCredential.getEmail());
		if (account == null) {
			throw new BusinessException("账号不存在");
		}
		UserVO user = userService.getViewObject(account.getUserId(), UserVO.class);
		if (user == null) {
			throw new BusinessException("账号不存在");
		}
		String token = this.createTokenAndLogin(account, user);
		user.setToken(token);
		userService.updateEntity(user);
		return user;
	}

	public <T extends BaseLoginUser> String createTokenAndLogin(Account account, T user) throws Exception {
		Subject subject = SecurityUtils.getSubject();
		String token = JwtUtil.createJWT(account.getAuthAccount(), "back", user.getClass().getSimpleName(), authProperties.getExpireTime() * 1000);
		LoginUser<?> userVOLoginUser = this.renderLoginUser(account, user);
		AuthToken authToken = new AuthToken(token, this.renderLoginUser(account, user));
		redisCacheManager.setCacheObject(token, userVOLoginUser, authProperties.getExpireTime(), TimeUnit.MINUTES);
		try {
			subject.login(authToken);
		} catch (UnknownAccountException e) {
			throw new BusinessException("账号不存在");
		} catch (IncorrectCredentialsException e) {
			throw new BusinessException("密码错误");
		}
		return token;
	}

	public void logout() {
		LoginUser<?> currentUser = SecurityUtils.getLoginUser();
		if (currentUser != null) {
			redisCacheManager.deleteObject(currentUser.getUser().getToken());
		}
		SecurityUtils.getSubject().logout();
	}

	public <T extends BaseLoginUser> LoginUser<T> renderLoginUser(Account account, T user) {
		return new LoginUser<>(account, user);
	}

	public <T extends BaseLoginUser> LoginUser<T> renderLoginUser(Account account, T user, Set<String> roles, Set<String> permissions) {
		return new LoginUser<>(account, user, roles, permissions);
	}
}
